
#include <omp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "TextManipulator.h"

int verificaPrimo(int n);

int main(int argc, char *argv[]){

	unsigned long int i;
	int size, j, n;
	double  tempopalin, tempoprimo;
	clock_t iniciopalin, inicioprimo, fimpalin, fimprimo;
	char *straux = NULL;
	char **palavra = NULL;
	unsigned long int npalavras,  qtpalin, qtprimo;;
	double sum = 0.0, primo = 0.0;

	/*Verifica se o programa foi chamdo corretamente*/
	if(argc != 2) {
		printf("Usage: <program> <path_file>\n");
        	return 1;
    	}
	else if(InitializeStructFile(argv[1])) {
	        printf("ERROO!!\n");
		return 1;
    	}
    	
	/*Aloca a matriz com as palavras*/   	 	
    	ResetFlags();
    	palavra = malloc(AMOUNTW_THREAD*sizeof(char*));
	for(i = 0; i < AMOUNTW_THREAD; i++)
		palavra[i] = malloc((WORDSIZE)*sizeof(char));		

	npalavras = ReturnWords(palavra); /*Obtem a quantidade de palavras na matriz*/


	/**printf("npalavras: %lu\n", npalavras);*/


	/*i eh compartilhado. j, size e straux sao privadas em cada thread
	npalavras mantem o valor da thread mestre
	*/
	#pragma omp parallel shared(i) private(j, size, straux, n, qtpalin, qtprimo, fimpalin, fimprimo, iniciopalin, inicioprimo) reduction(+:sum) reduction (+:primo), reduction(+:tempopalin), reduction(+:tempoprimo)
	{
		#pragma omp firtprivate(npalavras)
		
		printf("nro de threads: %d\n", omp_get_num_threads());
	
		/*straux ira conter a string inversa*/
		straux = (char*)malloc(sizeof(char)*WORDSIZE);
		qtpalin = 0;
		qtprimo = 0;
		tempopalin = 0;
		tempoprimo = 0;
		#pragma omp for
		for(i = 0; i < npalavras; i++){
			qtpalin++;

			size = strlen(palavra[i]) - 1;
		
			if((size+1) < WORDSIZE){
				iniciopalin = clock();
				for(j = 0; j < (size+1); j++)
					straux[j] = palavra[i][size-j];
				straux[j] = '\0';

				/*verifica se sao palindromos (nao eh case-sensitive)*/

				if(strcmp(straux, palavra[i]) == 0){
					/*medindo o tempo de calculo do palindromo*/
					fimpalin = clock();
					tempopalin = tempopalin + 1000 * (fimpalin - iniciopalin)/((double)CLOCKS_PER_SEC) ;		
					
					sum++;
					qtprimo++;
					inicioprimo = clock();
					n = 0;
					for(j = 0; j < (size+1); j++){
						n = (int) palavra[i][j] + n;
					}
					if(verificaPrimo(n))
						primo++;
					/*medindo o tempo de calculo do nro primo1*/
					fimprimo = clock();
					tempoprimo = tempoprimo + 1000 * (fimprimo - inicioprimo)/((double)CLOCKS_PER_SEC);
					/**printf("\"%s\" eh palindromo\n", palavra[i]);**/

				}
				else
					fimpalin = clock();
					tempopalin = tempopalin + 1000 * (fimpalin - iniciopalin)/((double)CLOCKS_PER_SEC);	
			}
		}/*fim for*/
		
		printf("thread %d: trabalhou em %lu palavras\n", omp_get_thread_num(), qtpalin);		
		printf("thread %d: verificou %lu vezes se era primo\n", omp_get_thread_num(), qtprimo);
		
	}/*Fim da regiao paralela*/
	printf("\n***********************************************\n");
	printf("qntadede palindromo: %.0lf\n", sum);
	printf("Tempo para calcular: %.2lf segundos\n", tempopalin/1000);
	printf("qntade de primo: %.0lf\n", primo);
	printf("Tempo para calcular: %.2lf segundos\n", tempoprimo/1000);
	
	return EXIT_SUCCESS;
}


int verificaPrimo(int n)
{
  // iterador, limitante, divisor atual, lista dos os candidatos a primo
  int i, Upper, Div, Aux, lsPrm[9999];

  // informa manualmente o primeiro primo, 2
  lsPrm[2] = 1;
  for(i = 3; i<=n; i+=2)
  {
    // considera inteiros impares como primos e inteiros pares como compostos de outros números
    lsPrm[i] = 1;
    lsPrm[i + 1] = 0;
  }

  // inicia o primeiro divisor primo: 3
  Div = 3;
  // descarta multiplos maiores que a raíz de n
  Upper = (int)sqrt((double)n);

// divide o problema em threads
#pragma omp parallel shared(Div, n, Upper) private(Aux, i)
{
  do
  {
    if (Div <= Upper)
    {
    // entrando numa região crítica; a variável Div é compartilhada entre as threads
    #pragma omp critical
      {
        // usa o valor do divisor atual em uma variável e incrementa a original para que outra thread use
        Aux = Div;
        Div += 2;
      }

  // ignora múltiplos de números compostos
  if (lsPrm[Aux])
     for (i = Aux; i * Aux <= n; i += 2)
        lsPrm[i * Aux] = 0;
  }
    else break;
  }
  while (1);
}

 if(lsPrm[n])
     return 1;
 else
     return 0;
}

